#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <sys/poll.h>
#include "inet.hpp"
#include "easylog.hpp"

namespace server
{

const int   default_port    = 8080;
const int   default_size    = 20;
const int   default_timeout = -1;
const int   default_fd      = -1;
const short default_event   = 0;

class poll_server : public inet::tcp::server
{
public:
    poll_server(uint16_t port)
        : server(port)
        , _fds(new struct pollfd[default_size])
        , _cap(0)
        , _timeout(default_timeout)
    {
        pollfd_arr_init();
    }

    void pollfd_arr_init()
    {
        for (int i = 0; i < default_size; i++) pollfd_init(_fds[i]);
    }

    void pollfd_init(struct pollfd& pf)
    {
        pf.fd = default_fd;
        pf.events = default_event;
        pf.revents = default_event;
    }
    void pollfd_clear(struct pollfd& pf)
    {
        pf.fd = default_fd;
        pf.events = default_event;
        pf.revents = default_event;
    }

    void start()
    {
        _fds[0].fd = _sock;
        _fds[0].events = POLLIN;
        ++_cap;

        while (true)
        {
            int timeout = _timeout;

            switch (poll(_fds.get(), _cap, timeout))
            {
            case 0:
                INFO("time out: %d", timeout);
                break;
            case -1:
                ERROR("select error, %d %s", errno, strerror(errno));
                break;
            default:
                event_handler();
                break;
            }
        }
    }

private:
    void event_handler()
    {
        for (int i = 0; i < _cap; i++)
        {
            auto& fd = _fds[i].fd;
            auto& revents = _fds[i].revents;

            if (fd == default_fd)
                assert(false);

            if (revents & POLLIN)
            {
                if (fd == _sock)
                {
                    acceptor();
                }
                else
                {
                    std::string buf;
                    recver(i, &buf);
                }
            }
            // if (revents & POLLOUT)
            // {
            //     std::string msg = "test";
            //     sender(i, msg);
            // }
            if (revents & POLLERR)
            {
                WARN("excepton event occurred, fd: %d", fd);
            }
        }
    }

    void acceptor()
    {
        std::string cip;
        uint16_t cport;
        int newfd = accept(&cip, &cport);

        if (_cap >= default_size)
        {
            close(newfd);
            WARN("connect close, fd array is full");
            return;
        }

        for (int i = 0; i < default_size; i++)
        {
            if (_fds[i].fd == default_fd) 
            {
                _fds[i].fd = newfd;
                _fds[i].events = POLLIN | POLLOUT;
                _cap++;
                break;
            }
        }

        INFO("a connect %d has been accepted [%s:%d]", newfd, cip.c_str(), cport);
    }

    void recver(int i, std::string* buf)
    {
        ssize_t s = recv(_fds[i].fd, buf, 1024);
        if (s > 0)
        {
            std::cout << *buf << std::endl;
        }
        else
        {
            if (s == 0) INFO("client quit");
            else WARN("recv error, %d %s", errno, strerror(errno));

            close(_fds[i].fd);
            pollfd_clear(_fds[i]);
            --_cap;
        }
    }

    void sender(int i, const std::string& msg)
    {
        size_t s = send(_fds[i].fd, msg);

        if (s <= 0)
        {
            if (s == 0) INFO("client quit");
            else WARN("send error, %d %s", errno, strerror(errno));

            close(_fds[i].fd);
            pollfd_clear(_fds[i]);
            --_cap;
        }
    }

private:
    std::unique_ptr<struct pollfd[]> _fds;
    int _cap;
    int _timeout;
};

} // namespace server
